home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-09-22 | 84.9 KB | 2,693 lines |
- _ROLL YOUR OWN DOS EXTENDER_
- by Al Williams
-
- [LISTING ONE]
-
- ;********************************************************************
- ;* PROT - A 386 protected mode DOS extender *
- ;* Copyright (C) 1989, by Al Williams All rights reserved. *
- ;* Permission is granted for non-commercial use of this software. *
- ;* You are expressly prohibited from selling this software, *
- ;* distributing it with another product, or removing this notice. *
- ;* If you distribute this software to others in any form, you must *
- ;* distribute all of the files that are listed below: *
- ;* PROT.ASM - The main routines and protected mode support. *
- ;* EQUMAC.INC - Equates and macros. *
- ;* STACKS.INC - Stack segments. *
- ;* GDT.INC - Global descriptor table. *
- ;* INT386.INC - Protected mode interrupt handlers. *
- ;* PMDEMO.PM - Example user code. *
- ;* PMPWD.PM - Alternate example code. *
- ;* FBROWSE.PM - Complete sample application. *
- ;* TSS.INC - Task state segments. *
- ;* CODE16.INC - 16 bit DOS code (entry/exit). *
- ;* PMASM.BAT - MASM driver for assembling PROT programs. *
- ;* To assemble: MASM /DPROGRAM=pname PROT.ASM,,PROT.LST; *
- ;* To link: LINK PROT; *
- ;* pname is the program name (code in pname.PM) *
- ;* if pname is ommited, USER.PM is used *
- ;* The resulting .EXE file is executable from the DOS prompt. *
- ;* This file is: PROT.ASM, the main protected mode code. *
- ;********************************************************************
-
- .XLIST
- .LALL
-
- .386P
-
- ; Name program if PROGRAM is defined
- IFDEF PROGRAM
- VTITLE MACRO PNAME ; temporary macro to title program
- TITLE PNAME
- ENDM
- VTITLE %PROGRAM
- PURGE VTITLE ; delete macro
- ; equates and macros
- INCLUDE EQUMAC.INC
-
- ; stack segments
- INCLUDE STACKS.INC
-
- ; Global descriptor table definitons
- INCLUDE GDT.INC
-
- ; interrupt code
- INCLUDE INT386.INC
-
- ; this is required to find out how large PROT is
- ZZZGROUP GROUP ZZZSEG
-
- ;********************************************************************
- ; 32 bit data segment
- DAT32 SEGMENT PARA PUBLIC 'DATA32' USE32
- DAT32BEG EQU $
-
- ; 32 bit stack values
- SLOAD DD OFFSET SSEG321-1
- SSLD DW SEL_STACK
-
- ; This location will hold the address for the PMODE IDT
- NEWIDT EQU THIS FWORD
- DW (IDTEND-IDTBEG)-1
- IDTB DD 0 ; filled in at runtime
-
- ; PSP segment address
- _PSP DW 0
-
- ; video variables for the OUCH and related routines
- CURSOR DD 0 ; cursor location
- COLOR DB 7 ; display cursor
-
- ; temp vars for some non reentrant interrupt routines
- STO1 DD 0
- STO2 DD 0
- STO3 DD 0
- STO4 DD 0
- SAV_DS DD 0
- SAV_ES DD 0
- SAV_GS DD 0
- SAV_FS DD 0
-
- BPON DB 0 ; Enables conditional breakpoints
-
- ; Debug Dump variables
- DUMP_SEG DW 0 ; if zero don't dump memory
- DUMP_OFF DD 0 ; Offset to start at
- DUMP_CNT DD 0 ; # of bytes to dump
-
- ; Break & critical error handler variables
- BREAKKEY DB 0 ; break key occurred
- CRITICAL DB 0 ; critical error occured
- CRITAX DW 0 ; critical error ax
- CRITDI DW 0 ; critical error di
- CRITBP DW 0 ; critical error bp
- CRITSI DW 0 ; critical error si
-
- ; Address of user's break handler
- BREAK_HANDLE EQU THIS FWORD
- BRK_OFF DD 0
- BRK_SEG DW 0
-
- ; Address of user's critical error handler
- CRIT_HANDLE EQU THIS FWORD
- CRIT_OFF DD OFFSET DEF_CRIT
- CRIT_SEG DW SEL_CODE32
-
- ; Message for default critical error handler
- CRITMSG DB 'A critical error has occured.',13,10
- DB '<A>bort, <R>etry, <F>ail? $'
-
- ; here is where vm86 int's stack up pl0 esp's
- INTSP DD $+PVSTACK+4
- DB PVSTACK DUP (0)
-
- ; Default VM86CALL parameter block
- PINTFRAME VM86BLK <>
-
- ; interface block for critical error handler
- CINTFRAME VM86BLK <>
-
- ; hardware interrupt vm86 block
- HINTFRAME VM86BLK <>
-
- ; storage for the original PIC interrupt mask registers
- INTMASK DB 0
- INTMASKAT DB 0
-
- DAT32END EQU $
- DAT32 ENDS
-
- ;********************************************************************
- ; Begin 32 bit code segment
-
- SEG32 SEGMENT PARA PUBLIC 'CODE32' USE32
- ASSUME CS:SEG32, DS:DAT32
- PCODE PROC
- SEG32BEG EQU $
-
- ; Start of protected mode code. We jump here from inside CODE16.INC
- SEG32ENT: MOV AX,SEL_DATA ; 1st order of business:
- MOV DS,AX ; load up segment registers
- LSS ESP, FWORD PTR SLOAD
- MOV AX,SEL_VIDEO
- MOV ES,AX
- MOV AX,SEL_DATA0
- MOV FS,AX
- MOV AX,SEL_GDT
- MOV GS,AX
- ; set up IDT
- CALL32S MAKIDT
- ; reprogram pic(s)
- IN AL,21H
- MOV INTMASK,AL
- IF ATCLASS
- IN AL,0A1H
- MOV INTMASKAT,AL
- MOV AL,11H
- OUT 0A0H,AL
- OUT 20H,AL
- IDELAY
- MOV AL,28H
- OUT 0A1H,AL
- MOV AL,20H
- OUT 21H,AL
- IDELAY
- MOV AL,2
- OUT 0A1H,AL
- MOV AL,4
- OUT 21H,AL
- IDELAY
- MOV AL,1
- OUT 0A1H,AL
- OUT 21H,AL
- IDELAY
- MOV AL,INTMASKAT
- OUT 0A1H,AL
- MOV AL,INTMASK
- OUT 21H,AL
- ELSE
- ; INBOARD PC Code
- MOV AL,13H
- OUT 20H,AL
- MOV AL,20H
- OUT 21H,AL
- MOV AL,9
- OUT 21H,AL
- MOV AL,INTMASK
- OUT 21H,AL
- ENDIF
- STI ; enable interrupts
-
- ; *** Start user code with TSS (req'd for vm86 op's etc.)
- MOV AX,TSS0
- LTR AX
- JMPABS32 TSS1,0
- PCODE ENDP
-
- ;*** 32 bit support routines
- ; This routine creates the required IDT. This is only a subroutine to keep
- ; from cluttering up the main code, since you aren't likely to call it again.
- ; Assumes that all ISR routines are of fixed length and in sequence. After
- ; makidt has built the table, you can still replace individual INT gates with
- ; your own gates (see make_gate)
- MAKIDT PROC NEAR
- PUSH ES
- MOV AX,IDTABLE
- MOVZX EAX,AX
- SHL EAX,4
- ADD EAX,OFFSET IDTBEG
- MOV IDTB,EAX
- MOV AX,SEL_IDT
- MOV ES,AX
- XOR AL,AL
- ; Make all interrupt gates DPL=3
- MOV AH,INTR_GATE OR DPL3
- MOV CX,SEL_ICODE
- MOV EDX,OFFSET IDTBEG
- XOR SI,SI
- MOV EBX,OFFSET INT0
- IDTLOOP: CALL32F SEL_CODE32,MAKE_GATE
- ADD EBX,INT1-INT0
- ADD SI,8
- ; loop form max # of interrupts
- CMP SI,(TOPINT+1)*8
- JB SHORT IDTLOOP
- LIDT NEWIDT
- POP ES
- RET
- MAKIDT ENDP
-
- ; This routine is just like the real mode make_desc
- ; EBX=base ECX=limit AH=ARB AL=0 or 1 for 16 or 32 bit
- ; SI=selector (TI&RPL ignored) and ES:EDX is the table base address
- MAKE_SEG PROC FAR
- PUSH ESI
- PUSH EAX
- PUSH ECX
- MOVZX ESI,SI
- SHR SI,3 ; adjust to slot #
- SHL AL,6 ; shift size to right bit position
- CMP ECX,0FFFFFH ; see if you need to set G bit
- JLE OKLIM
- SHR ECX,12 ; div by 4096
- OR AL,80H ; set G bit
- OKLIM: MOV ES:[EDX+ESI*8],CX
- SHR ECX,16
- OR CL,AL
- MOV ES:[EDX+ESI*8+6],CL
- MOV ES:[EDX+ESI*8+2],BX
- SHR EBX,16
- MOV ES:[EDX+ESI*8+4],BL
- MOV ES:[EDX+ESI*8+5],AH
- MOV ES:[EDX+ESI*8+7],BH
- POP ECX
- POP EAX
- POP ESI
- RET
- MAKE_SEG ENDP
-
- ; This routine make gates -- AL=WC if applicable -- AH=ARB -- EBX=offset
- ; CX=selector -- ES:EDX=table base -- SI= selector (TI&RPL ignored)
- MAKE_GATE PROC FAR
- PUSH ESI
- PUSH EBX
- SHR SI,3
- MOVZX ESI,SI
- MOV ES:[EDX+ESI*8],BX
- MOV ES:[EDX+ESI*8+2],CX
- MOV ES:[EDX+ESI*8+4],AX
- SHR EBX,16
- MOV ES:[EDX+ESI*8+6],BX
- POP EBX
- POP ESI
- RET
- MAKE_GATE ENDP
-
- ; Routine to call BIOS/DOS. NOT REENTRANT (but so what? DOS isn't either)
- CALL86 PROC FAR
- PUSH DS
- PUSH GS
- PUSH FS
- RETRY86:
- PUSHAD
- PUSHFD
- PUSH ES:[EBX+40] ; save new ebx
- PUSH EBX
- PUSH ES
- INT 30H ; call PROT
- PUSH SEL_DATA
- POP DS
- POP ES
- XCHG EBX,[ESP]
- POP ES:[EBX+40]
- PUSHFD
- CMP BREAKKEY,0 ; see if break occured
- JZ SHORT NOBRKCHECK
- CMP BRK_SEG,0 ; see if user has brk handler
- JZ SHORT NOBRKCHECK
- ; call user's break handler
- MOV BREAKKEY,0
- CALL FWORD PTR BREAK_HANDLE
- NOBRKCHECK:
- CMP CRITICAL,0 ; see if critical error
- JZ SHORT NOCRITCK
- CMP CRIT_SEG,0 ; see if critical error handler
- JZ SHORT NOCRITCK
- ; call critical error handler
- PUSH EAX
- XOR AL,AL
- MOV CRITICAL,AL
- CALL FWORD PTR CRIT_HANDLE
- OR AL,AL ; AL=0? FAIL
- JNZ SHORT RETRY?
- POP EAX
- POPFD
- STC ; make sure carry is set
- PUSHFD
- JMP SHORT NOCRITCK
- RETRY?: DEC AL ; AL=1? RETRY
- JNZ SHORT CABORT
- ; To retry an error, we set up everything the way it was and
- ; redo the interrupt. This is cheating (a little), and may not
- ; work in every possible case, but it seems to work in all the cases tried.
- POP EAX
- POPFD
- POP ES:[EBX+40]
- POPFD
- POPAD
- JMP SHORT RETRY86
- CABORT: POP EAX ; ABORT
- POPFD
- LEA ESP,[ESP+40] ; balance stack
- MOV AL,7FH ; DOS error=7FH
- BACK2DOS
- NOCRITCK:
- POPFD
- LEA ESP,[ESP+40] ; balance stack
- PUSHFD
- ; see if segment save requested
- CMP BYTE PTR ES:[EBX],0
- JZ NOSEGS
- ; load parameter block from static save area
- PUSH EAX
- MOV EAX,SAV_FS
- MOV ES:[EBX+28],EAX
- MOV EAX,SAV_DS
- MOV ES:[EBX+24],EAX
- MOV EAX,SAV_ES
- MOV ES:[EBX+20],EAX
- MOV EAX,SAV_GS
- MOV ES:[EBX+32],EAX
- POP EAX
- NOSEGS:
- POPFD
- POP FS
- POP GS
- POP DS
- MOV EBX,ES:[EBX+40]
- RET
- CALL86 ENDP
-
- ; Directly clear page 0 of the screen
- CLS PROC FAR
- PUSHFD
- PUSH DS
- PUSH ES
- PUSH EDI
- PUSH ECX
- PUSH EAX
- MOV CX,SEL_VIDEO
- MOV ES,CX
- MOV CX,SEL_DATA
- MOV DS,CX
- CLD
- MOV EDI,0
- MOV ECX,2000
- MOV AX,0720H
- REP STOSW
- XOR ECX,ECX
- MOV CURSOR,ECX
- POP EAX
- POP ECX
- POP EDI
- POP ES
- POP DS
- POPFD
- RET
- CLS ENDP
-
- ; Outputs message to screen -- ASCIIZ pointer in ds:ebx - modifies ebx
- MESSOUT PROC FAR
- PUSH EAX
- NXT: MOV AL,[EBX]
- INC EBX
- OR AL,AL
- JNZ SHORT SKIP
- POP EAX
- RET
- SKIP: CALL32F SEL_CODE32, OUCH
- JMP SHORT NXT
- MESSOUT ENDP
-
- ; Performs CR/LF sequence to screen using OUCH
- CRLF PROC FAR
- PUSH EAX
- MOV AL,13
- CALL32F SEL_CODE32,OUCH
- MOV AL,10
- CALL32F SEL_CODE32,OUCH
- POP EAX
- RET
- CRLF ENDP
-
- ; Character and digit output routines
- ; hexout4 - print longword in EAX in hex
- ; hexout2 - print word in AX in hex
- ; hexout - print byte in AL in hex
- ; ouch - print ASCII character in AL
- OUTPUT PROC FAR
- ; print longword in eax
- HEXOUT4 LABEL FAR
- PUSH EAX
- SHR EAX,16
- CALL32F SEL_CODE32,HEXOUT2
- POP EAX
- ; print word in ax
- HEXOUT2 LABEL FAR
- PUSH EAX
- MOV AL,AH
- CALL32F SEL_CODE32, HEXOUT
- POP EAX
- ; print a hex byte in al
- HEXOUT LABEL FAR
- MOV BL,AL
- AND AX,0F0H
- SHL AX,4
- MOV AL,BL
- AND AL,0FH
- ADD AX,'00'
- MOV BL,AL
- MOV AL,AH
- CALL32F SEL_CODE32, HEX1DIG
- MOV AL,BL
- HEX1DIG: CMP AL,'9'
- JBE SHORT H1DIG
- ADD AL,'A'-'0'-0AH
- H1DIG:
- OUCH LABEL FAR
- PUSH EDI
- PUSH EAX
- PUSH DS
- PUSH ES
- PUSH ECX
- MOV CX,SEL_VIDEO
- MOV ES,CX
- MOV CX,SEL_DATA
- MOV DS,CX
- POP ECX
- MOV AH,COLOR
- MOV EDI,CURSOR
- CMP EDI,2000 ; rolling off the screen?
- JB NOSCROLL
- ; scroll screen if required
- PUSH DS
- PUSH ES
- POP DS
- PUSH ESI
- PUSH ECX
- PUSH EDI
- CLD
- MOV ECX,960
- XOR EDI,EDI
- MOV ESI,160
- REP MOVSD
- POP EDI
- SUB EDI,80
- POP ECX
- POP ESI
- POP DS
- NOSCROLL: CMP AL,0DH
- JZ SHORT CR
- CMP AL,0AH
- JZ SHORT LF
- ; write to screen
- MOV ES:[EDI*2],AX
- INC EDI
- JMP SHORT OUCHD
- CR: PUSH EDX
- PUSH ECX
- MOV EAX,EDI
- XOR EDX,EDX
- MOV ECX,80
- DIV ECX
- SUB EDI,EDX
- POP ECX
- POP EDX
- JMP SHORT OUCHD
- LF: ADD EDI,50H
- OUCHD: MOV CURSOR,EDI ; update cursor
- POP ES
- POP DS
- POP EAX
- POP EDI
- RET
- OUTPUT ENDP
- ; Default critical error handler
- DEF_CRIT PROC FAR
- PUSH ES
- PUSH EBX
- PUSH EDX
- MOV BX,SEL_DATA
- MOV ES,BX
- ASSUME DS:NOTHING, ES:DAT32
- ; load critical error handler's private stack
- MOV BX,CSTACK
- MOV CINTFRAME.VMSS,EBX
- MOV EBX,OFFSET CSTACK
- MOV CINTFRAME.VMESP,EBX
- MOV BX,DAT32
- MOV CINTFRAME.VMDS,EBX
- MOV BX,21H
- MOV CINTFRAME.VMINT,EBX
- MOV EBX, OFFSET CINTFRAME
- MOV EDX,OFFSET CRITMSG
- MOV AH,9
- PUSH EBX
- VM86CALL ; print message
- POP EBX
- CLOOP:
- MOV AH,7
- PUSH EBX
- VM86CALL ; get keystroke
- POP EBX
- ; ignore function keys
- OR AL,AL
- JZ SHORT CRITFNKEY
- MOV AH,AL
- OR AL,20H ; convert to lower case
- CMP AL,'a'
- JNZ SHORT CFAIL?
- MOV AL,2
- JMP SHORT CREXIT
- CFAIL?: CMP AL,'f'
- JNZ SHORT CRETRY?
- XOR AL,AL
- JMP SHORT CREXIT
- CRETRY?:
- CMP AL,'r'
- MOV AL,1
- JNZ SHORT CRITBAD
- CREXIT: MOV DL,AH ; echo letter + CRLF
- MOV AH,2
- PUSH EAX
- PUSH EBX
- VM86CALL
- POP EBX
- MOV AH,2
- MOV DL,0DH
- PUSH EBX
- VM86CALL
- POP EBX
- MOV AH,2
- MOV DL,0AH
- VM86CALL
- POP EAX
- POP EDX
- POP EBX
- POP ES
- RET
- CRITFNKEY:
- MOV AH,7
- PUSH EBX
- VM86CALL ; ignore fn key/alt-key
- POP EBX
- CRITBAD:
- MOV DL,7
- MOV AH,2
- PUSH EBX
- VM86CALL ; unknown input - ring bell
- POP EBX
- JMP SHORT CLOOP
- DEF_CRIT ENDP
-
- SEG32END EQU $
- SEG32 ENDS
-
- ;********************************************************************
- ; user program - PROT includes the file defined by the variable PROGRAM.
- ; convoluted method to make MASM take a string equate for an include filename
-
- TEMPINCLUDE MACRO FN ; ; temporary macro
- INCLUDE &FN&.PM
- ENDM
- TEMPINCLUDE %PROGRAM
-
- PURGE TEMPINCLUDE ; delete macro
-
- ; task state segments
- INCLUDE TSS.INC
-
- ; 16 bit code (DOS entry/exit)
- INCLUDE CODE16.INC
-
- ; Segment to determine the last memory address
- ZZZSEG SEGMENT PARA PUBLIC 'ZZZ' USE16
- ZZZSEG ENDS
- ELSE
- IF2
- %OUT You must specify a program title
- %OUT use: MASM /DPROGRAM=PNAME PROT.ASM...
- ENDIF
- .ERR
- ENDIF
- END ENTRY
-
-
- [LISTING TWO]
-
- ;********************************************************************
- ;* PROT - A 386 protected mode DOS extender *
- ;* Copyright (C) 1989, by Al Williams -- All rights reserved. *
- ;* Permission is granted for non-commercial use of this software *
- ;* subject to certain conditions (see PROT.ASM). *
- ;* This file is: GDT.INC, the Global Descriptor Table definitions. *
- ;********************************************************************
- ; See EQUMAC.INC for an explanation of the DESC macro
- GDTSEG SEGMENT PARA PUBLIC 'CODE32' USE32
- GDT EQU $ ; GDT space
- DESC SEL_NULL ; DUMMY NULL SELECTOR
- DESC SEL_CODE16 ; 16 BIT CODE SEGMENT
- DESC SEL_DATA0 ; 4GB SEGMENT
- DESC SEL_CODE32 ; 32 BIT CODE SEGMENT
- DESC SEL_STACK ; 32 BIT STACK
- DESC SEL_RDATA ; REAL MODE LIKE DATA SEG
- DESC SEL_GDT ; GDT ALIAS
- DESC SEL_VIDEO ; VIDEO MEMORY
- DESC SEL_DATA ; 32 BIT DATA
- DESC SEL_IDT ; IDT ALIAS
- DESC SEL_ICODE ; ISR SEGMENT
- DESC SEL_TSS0 ; DUMMY TASK BLOCK
- DESC TSS0 ; SAME (MUST FOLLOW SEL_TSS0)
- DESC SEL_TSS1 ; MAIN TASK BLOCK
- DESC TSS1 ; SAME (MUST FOLLOW SEL_TSS1)
- DESC SEL_UCODE ; USER CODE
- DESC SEL_UDATA ; USER DATA
- DESC SEL_PSP ; DOS PSP
- DESC SEL_FREE ; FREE DOS MEMORY
- DESC SEL_EXT ; EXTENDED MEMORY
- DESC SEL_ENV ; ENVIROMENT
- GDTEND = $
- GDTSEG ENDS
-
-
- [LISTING THREE]
-
- ;********************************************************************
- ;* PROT - A 386 protected mode DOS extender *
- ;* Copyright (C) 1989, by Al Williams -- All rights reserved. *
- ;* Permission is granted for non-commercial use of this software *
- ;* subject to certain conditions (see PROT.ASM). *
- ;* This file is: EQUMAC.INC, assorted macros and equates. *
- ;********************************************************************
- ; EQUates the user may wish to change
- ATCLASS EQU 1 ; 1=AT/386 0=INBOARD 386/PC
- DOSSTACK EQU 200H ; stack size for DOS startup
- VM86STACK EQU 200H ; stack size for VM86 int calls
- CRITSTACK EQU 30H ; stack size for crit err handler
- PMSTACK EQU 1000H ; stack size for p-mode stack
- PVSTACK EQU 260 ; pl0/vm86 psuedo stack size
- ; Maximum protected mode interrupt # defined
- TOPINT EQU 30H
- ; The critical error handler works different for DOS 2.X than for other DOS
- ; versions. In 99% of the cases it won't make any difference if you compile
- ; with DOS=2.... major dos version number (2, 3 or 4)
- DOS EQU 3
-
- ; parameter block to interface for int 30H (call86 & VM86CALL)
- VM86BLK STRUC
- VMSEGFLAG DD 0 ; restore segment registers (flag)
- VMINT DD 0 ; interrupt number
- VMFLAGS DD 0 ; EFLAGS
- VMESP DD 0 ; ESP
- VMSS DD 0 ; SS
- VMES DD 0 ; ES
- VMDS DD 0 ; DS
- VMFS DD 0 ; FS
- VMGS DD 0 ; GS
- VMEBP DD 0 ; EBP
- VMEBX DD 0 ; EBX
- VM86BLK ENDS
-
- ; Access rights equates. Use these with make_desc or make_seg
- RO_DATA EQU 90H ; r/o data
- RW_DATA EQU 92H ; r/w data
- RO_STK EQU 94H ; r/o stack
- RW_STK EQU 96H ; r/w stack
- EX_CODE EQU 98H ; exec only code
- ER_CODE EQU 9AH ; read/exec code
- CN_CODE EQU 9CH ; exec only conforming code
- CR_CODE EQU 9EH ; read/exec conforming code
- LDT_DESC EQU 82H ; LDT entry
- TSS_DESC EQU 89H ; TSS entry
-
- ; use these with make_gate
- CALL_GATE EQU 8CH ; call gate
- TRAP_GATE EQU 8FH ; trap gate
- INTR_GATE EQU 8EH ; int gate
- TASK_GATE EQU 85H ; task gate
-
- ; dpl equates
- DPL0 EQU 0
- DPL1 EQU 20H
- DPL2 EQU 40H
- DPL3 EQU 60H
-
- ; macro definitons
-
- ; other macros use this to error check parameters
- ; Give an error if last is blank or toomany is not blank
- ERRCHK MACRO LAST,TOOMANY
- IFNB <TOOMANY>
- IF2
- %OUT Too many parameters
- ENDIF
- .ERR
- ENDIF
- IFB <LAST>
- IF2
- %OUT Not enough parameters
- ENDIF
- .ERR
- ENDIF
- ENDM
-
- ; Perform absolute 16 bit jump (in a 16 bit segment)
- JMPABS MACRO A,B,ERRCK
- ERRCHK B,ERRCK
- DB 0EAH ; ; absoulte 16 bit jump
- DW OFFSET B
- DW A
- ENDM
-
- ; Peform absolute 32 bit jump (in a 32 bit segment)
- JMPABS32 MACRO A,B,ERRCK
- ERRCHK B,ERRCK
- DB 0EAH ; ; absolute 32 bit jump
- DD OFFSET B
- DW A
- ENDM
- ; this generates a correct 32 bit offset for a proc call
- ; since MASM doesn't sign extend 32 bit relative items
- CALL32S MACRO LBL,ERRCK ; ; short call
- ERRCHK LBL,ERRCK
- DB 0E8H
- DD LBL-($+4)
- ENDM
-
- CALL32F MACRO SG,LBL,ERRCK ; ; far call
- ERRCHK LBL,ERRCK
- DB 9AH
- DD OFFSET LBL
- DW SG
- ENDM
-
- JMP32S MACRO LBL,ERRCK ; ; short jump
- ERRCHK LBL,ERRCK
- DB 0E9H
- DD LBL-($+4)
- ENDM
-
- ; jcc32 uses condition codes used in Intel literature conditional jump macro
- JCC32 MACRO CONDX,LBL,ERRCK
- ERRCHK LBL,ERRCK
- DB 0FH
- IFIDNI <CONDX>,<A>
- DB 87H
- ELSEIFIDNI <CONDX>,<NBE>
- DB 87H
- ELSEIFIDNI <CONDX>, <AE>
- DB 83H
- ELSEIFIDNI <CONDX>, <C>
- DB 82H
- ELSEIFIDNI <CONDX>, <NAE>
- DB 82H
- ELSEIFIDNI <CONDX>, <B>
- DB 82H
- ELSEIFIDNI <CONDX>, <BE>
- DB 86H
- ELSEIFIDNI <CONDX>, <E>
- DB 84H
- ELSEIFIDNI <CONDX>, <Z>
- DB 84H
- ELSEIFIDNI <CONDX>, <G>
- DB 8FH
- ELSEIFIDNI <CONDX>, <GE>
- DB 8DH
- ELSEIFIDNI <CONDX>, <L>
- DB 8CH
- ELSEIFIDNI <CONDX>, <LE>
- DB 8EH
- ELSEIFIDNI <CONDX>, <NA>
- DB 86H
- ELSEIFIDNI <CONDX>, <NB>
- DB 83H
- ELSEIFIDNI <CONDX>, <NC>
- DB 83H
- ELSEIFIDNI <CONDX>, <NGE>
- DB 8CH
- ELSEIFIDNI <CONDX>, <NL>
- DB 8DH
- ELSEIFIDNI <CONDX>, <NO>
- DB 81H
- ELSEIFIDNI <CONDX>, <NP>
- DB 8BH
- ELSEIFIDNI <CONDX>, <NS>
- DB 89H
- ELSEIFIDNI <CONDX>, <NZ>
- DB 85H
- ELSEIFIDNI <CONDX>, <O>
- DB 80H
- ELSEIFIDNI <CONDX>, <P>
- DB 8AH
- ELSEIFIDNI <CONDX>, <PE>
- DB 8AH
- ELSEIFIDNI <CONDX>, <PO>
- DB 8BH
- ELSEIFIDNI <CONDX>, <S>
- DB 88H
- ELSE
- %OUT JCC32: Unknown condition code
- .ERR
- ENDIF
- DD LBL-($+4)
- ENDM
-
- ; Override default operand size
- OPSIZ MACRO NOPARM ; ; op size overide
- ERRCHK X,NOPARM
- DB 66H
- ENDM
- ; Override default address size
- ADSIZ MACRO NOPARM ; ; address size overide
- ERRCHK X,NOPARM
- DB 67H
- ENDM
- ; delay macro for interrupt controller access
- IDELAY MACRO NOPARM
- LOCAL DELAY1,DELAY2
- ERRCHK X,NOPARM
- JMP SHORT DELAY1
- DELAY1: JMP SHORT DELAY2
- DELAY2:
- ENDM
-
- ; BREAKPOINT MACROS
-
- ; MACRO to turn on NBREAKPOINTS. If used with no arguments (or a 1), this
- ; macro makes NBREAKPOINT active if used with an argument > 1, NBREAKPOINT
- ; will break after that many passes
- BREAKON MACRO ARG,ERRCK
- ERRCHK X,ERRCK
- PUSH DS
- PUSH SEL_DATA
- POP DS
- PUSH EAX
- IFB <ARG>
- MOV AL,1
- ELSE
- MOV AL,&ARG
- ENDIF
- MOV BPON,AL
- POP EAX
- POP DS
- ENDM
- ; Turns off NBREAKPOINT
- BREAKOFF MACRO NOPARAM
- ERRCHK X,NOPARAM
- PUSH DS
- PUSH SEL_DATA
- POP DS
- PUSH EAX
- XOR AL,AL
- MOV BPON,AL
- POP EAX
- POP DS
- ENDM
- BREAKPOINT MACRO NOPARM
- ERRCHK X,NOPARM
- INT 3
- ENDM
- ; Counter breakpoint - use BREAKON to set count control
-
- ; BREAKPOINT with memory dump.
- ; usage: BREAKDUMP seg_selector, offset, number_of_words
- BREAKDUMP MACRO SEG,OFF,CNT,ERRCK
- ERRCHK CNT,ERRCK
- PUSH EAX
- PUSH DS
- MOV AX,SEL_DATA
- MOV DS,AX
- MOV AX,&SEG
- MOV DUMP_SEG,AX
- MOV EAX,OFFSET &OFF
- MOV DUMP_OFF,EAX
- MOV EAX,&CNT
- MOV DUMP_CNT,EAX
- POP DS
- POP EAX
- BREAKPOINT
- ENDM
- NBREAKDUMP MACRO SEG,OFF,CNT,ERRCK
- ERRCHK CNT,ERRCK
- LOCAL NONBP
- PUSH DS
- PUSH SEL_DATA
- POP DS
- PUSHFD
- OR BPON,0
- JZ NONBP
- DEC BPON
- JNZ NONBP
- POPFD
- POP DS
- BREAKDUMP SEG,OFF,CNT
- NONBP:
- POPFD
- POP DS
- ENDM
-
- ; determine linear address of first free byte of memory (to nearest paragraph)
- LOADFREE MACRO REG,ERRCK
- ERRCHK REG,ERRCK
- XOR E®,E®
- MOV ®,SEG ZZZGROUP
- SHL E®,4
- ENDM
-
- ; Set up PINTFRAME (uses eax). Loads vmstack & vmdata to the ss:esp and
- ; ds slots in pintframe -- default ss:esp=ssint1 -- default ds=userdata
- PROT_STARTUP MACRO VMSTACK,VMDATA,ERRCK
- ERRCHK X,ERRCK
- IFB <VMSTACK>
- MOV AX,SEG SSINT1
- ELSE
- MOV AX,SEG VMSTACK
- ENDIF
- MOV PINTFRAME.VMSS,EAX
- IFB <VMSTACK>
- MOV EAX, OFFSET SSINT1
- ELSE
- MOV EAX, OFFSET VMSTACK
- ENDIF
- MOV PINTFRAME.VMESP,EAX
- IFB <VMDATA>
- MOV AX,SEG USERDATA
- ELSE
- MOV AX,SEG VMDATA
- ENDIF
- MOV PINTFRAME.VMDS,EAX
- ENDM
-
- ; start PROT user segments
- PROT_CODE MACRO NOPARM
- ERRCHK X,NOPARM
- USERCODE SEGMENT PARA PUBLIC 'CODE32' USE32
- USERCODEBEG EQU $
- ASSUME CS:USERCODE, DS:USERDATA, ES:DAT32
- ENDM
-
- PROT_DATA MACRO NOPARM
- ERRCHK X,NOPARM
- USERDATA SEGMENT PARA PUBLIC 'DATA32' USE32
- USERDATABEG EQU $
- ENDM
-
- PROT_CODE_END MACRO NOPARM
- ERRCHK X,NOPARM
- USERCODEEND EQU $
- USERCODE ENDS
- ENDM
-
- PROT_DATA_END MACRO NOPARM
- ERRCHK X,NOPARM
- USERDATAEND EQU $
- USERDATA ENDS
- ENDM
-
- ; Simplfy programs with no data segment
- NODATA MACRO NOPARM
- ERRCHK X,NOPARM
- PROT_DATA
- PROT_DATA_END
- ENDM
-
- ; Mnemonic for call86 call
- VM86CALL MACRO NOPARM
- ERRCHK X,NOPARM
- CALL32F SEL_CODE32,CALL86
- ENDM
-
- ; Mnemonic for dos return
- BACK2DOS MACRO RC,ERRCK
- ERRCHK X,ERRCK
- IFNB <RC>
- MOV AL,RC
- ENDIF
- JMPABS32 SEL_CODE16,BACK16
- ENDM
-
- ; Variables and macro to create GDT/LDT/IDT entries
- C_GDT = 0
- C_LDT = 0
- C_IDT = 0
-
- ; create "next" descriptor with name in table. If no table specified, use GDT
- DESC MACRO NAME,TABLE,ERRCK
- DQ 0
- IFB <TABLE>
- NAME = C_GDT
- C_GDT = C_GDT+8
- ELSE
- IFIDNI <TABLE>,<LDT>
- ; For LDT selectors, set the TI bit to one
- NAME = C_&TABLE OR 4
- ELSE
- NAME = C_&TABLE
- ENDIF
- C_&TABLE = C_&TABLE+8
- ENDIF
- ENDM
-
-
- [LISTING FOUR]
-
- ;********************************************************************
- ;* *
- ;* PROT - A 386 protected mode DOS extender *
- ;* Copyright (C) 1989, by Al Williams *
- ;* All rights reserved. *
- ;* *
- ;* Permission is granted for non-commercial use of this software *
- ;* subject to certain conditions (see PROT.ASM). *
- ;* *
- ;* This file is: STACKS.INC, which contains all the stack segments. *
- ;* *
- ;********************************************************************
- ; 16 bit stack segment (for CODE16)
- SSEG SEGMENT PARA STACK 'STACK' USE16
- SSEG0 DB DOSSTACK DUP (?)
- SSEG1 EQU $
- SSEG ENDS
-
- ; 16 bit stack segment for vm86 int (both hardware & INT 30)
- SSINT SEGMENT PARA STACK 'STACK' USE16
- SSINT0 DB VM86STACK DUP (?)
- SSINT1 EQU $
- SSINT ENDS
-
- ; private stack for default critical error handler dos calls
- CSTACK SEGMENT PARA STACK 'STACK' USE16
- DB CRITSTACK DUP (?)
- CSTACK ENDS
-
-
- ; 32 bit stack segment
- SS32 SEGMENT PARA PUBLIC 'STACK' USE32
- SSEG32 DB PMSTACK DUP (?)
- SSEG321 EQU $
- SS32 ENDS
-
-
- [LISTING FIVE]
-
- ;********************************************************************
- ;* *
- ;* PROT - A 386 protected mode DOS extender *
- ;* Copyright (C) 1989, by Al Williams *
- ;* All rights reserved. *
- ;* *
- ;* Permission is granted for non-commercial use of this software *
- ;* subject to certain conditions (see PROT.ASM). *
- ;* *
- ;* This file is: INT386.INC *
- ;* *
- ;********************************************************************
-
- ; Peculiarities
- ; 1 - We don't emulate lock, IRETD, PUSHFD, POPFD yet
- ; 2 - When calling INT 25 or INT 26 from protected mode
- ; flags are destroyed (not left on stack as in VM86, real mode)
- ; 3 - For now I don't support adding offsets to the return address
- ; on your vm86 stack to change where IRET goes to. That could be
- ; fixed, but I don't know of any PC software that does that
-
-
- ; fake segment for far ret interrupts
- ; (this segment has no descriptor in GDT/LDT)
- QISR SEGMENT PARA PUBLIC 'CODE16' USE16
- ASSUME CS:QISR
- ; push sacrifical words for IRET to eat.
- ; PL0 stack controls return anyway
- QIRET:
- PUSH 0
- PUSH 0
- PUSH 0
- IRET
- QISR ENDS
-
- ; IDT segment
- IDTABLE SEGMENT PARA PUBLIC 'DATA32' USE32
- IDTBEG EQU $
- DQ TOPINT+1 DUP (0)
- IDTEND EQU $
- IDTABLE ENDS
-
-
- ;ISR segment
- DEFINT MACRO N
- INT&N LABEL FAR
- PUSH &N
- JMP NEAR PTR INTDUMP
- ENDM
-
-
- ISR SEGMENT PARA PUBLIC 'CODE32' USE32
- ASSUME CS:ISR
- ISRBEG EQU $
- ; This code defines interrupt handlers from 0 to TOPINT
- ; (TOPINT is defined in EQUMAC.INC)
- INTNO = 0
- REPT TOPINT+1
- DEFINT %INTNO
- INTNO = INTNO + 1
- ENDM
-
- ; Debug dump messages
- MESSAREA DB 'INT=',0
- STKM DB 'Stack Dump:',0
- TASKM DB ' TR=',0
- RTABLE DB 'G'
- DB 'F'
- DB 'D'
- DB 'E'
- GTABLE DB 'DISIBPSPBXDXCXAX'
- MEMMESS DB 'Memory Dump:',0
-
- ; All interrupts come here
- ; We check for the interrupt # pushed on the stack and
- ; vector accordingly. This adds some interrupt latency,
- ; but simplifies IDT construction.
- INTDUMP LABEL NEAR
- ; check for GP error
- CMP BYTE PTR [ESP],0DH
- JZ NEAR PTR INT13H
- NOT13:
- ; check for vm86 psuedo-int
- CMP BYTE PTR [ESP],30H
- JZ NEAR PTR INT30H
- ; hardware interrupt?
- CMP BYTE PTR [ESP],20H
- JB SHORT NOTIO
- IF ATCLASS
- CMP BYTE PTR [ESP],2FH
- ELSE
- CMP BYTE PTR [ESP],27H
- ENDIF
- JA SHORT NOTIO
- JMP NEAR PTR HWINT
- NOTIO:
- ; if we made it here, we have an unexpected interrupt
- ; so crank out a debug dump and exit to dos
- PUSHAD
- PUSH GS
- PUSH FS
- PUSH DS
- PUSH ES
- MOV AX,SEL_VIDEO
- MOV ES,AX
- MOV AX,CS
- MOV DS,AX
- ; do dump
- MOV ECX,4
- INTL1:
- MOV AL,[RTABLE-1+ECX]
- CALL32F SEL_CODE32,OUCH
- MOV AL,'S'
- CALL32F SEL_CODE32,OUCH
- MOV AL,'='
- CALL32F SEL_CODE32,OUCH
- POP EAX
- CALL32F SEL_CODE32,HEXOUT2
- PUSH ECX
- MOV ECX,6
- LSP1: MOV AL,' '
- CALL32F SEL_CODE32,OUCH
- LOOP LSP1
- POP ECX
- LOOP INTL1
- CALL32F SEL_CODE32,CRLF
- XOR ECX,ECX
- INTL2: CMP CL,5
- JNZ SHORT NOCRINT
- CALL32F SEL_CODE32,CRLF
- NOCRINT:
- MOV AL,'E'
- CALL32F SEL_CODE32,OUCH
- MOV AL,[GTABLE+ECX*2]
- CALL32F SEL_CODE32,OUCH
- MOV AL,[GTABLE+1+ECX*2]
- CALL32F SEL_CODE32,OUCH
- MOV AL,'='
- CALL32F SEL_CODE32,OUCH
- POP EAX
- CALL32F SEL_CODE32,HEXOUT4
- MOV AL,' '
- CALL32F SEL_CODE32,OUCH
- INC CL
- CMP CL,8
- JNE SHORT INTL2
- MOV EBX,OFFSET MESSAREA
- CALL32F SEL_CODE32,MESSOUT
- POP EAX
- CALL32F SEL_CODE32,HEXOUT
- MOV EBX,OFFSET TASKM
- CALL32F SEL_CODE32,MESSOUT
- STR AX
- CALL32F SEL_CODE32,HEXOUT2
- CALL32F SEL_CODE32,CRLF
-
- ; stack dump
- XOR EAX,EAX
- MOV AX,SS
- LSL EDX,EAX
- JNZ SHORT INTABT
- MOV EBX,OFFSET STKM
- CALL32F SEL_CODE32,MESSOUT
- XOR CL,CL
- INTL3: CMP ESP,EDX
- JAE SHORT INTABT
- TEST CL,7
- JNZ SHORT NOSCR
- CALL32F SEL_CODE32,CRLF
- NOSCR: POP EAX
- CALL32F SEL_CODE32,HEXOUT4
- INC CL
- MOV AL,' '
- CALL32F SEL_CODE32,OUCH
- JMP SHORT INTL3
-
- INTABT:
- ; Check for memory dump request
- MOV AX,SEL_DATA
- MOV DS,AX
- ASSUME DS:DAT32
- MOV AX,WORD PTR DUMP_SEG
- OR AX,AX
- JZ SHORT NOMEMDUMP
- ; come here to do memory dump
- CALL32F SEL_CODE32,CRLF
- PUSH DS
- PUSH CS
- POP DS
- MOV EBX,OFFSET MEMMESS
- CALL32F SEL_CODE32,MESSOUT
- CALL32F SEL_CODE32,CRLF
- POP DS
- MOV AX,WORD PTR DUMP_SEG
- MOV ES,AX
- CALL32F SEL_CODE32,HEXOUT2
- MOV AL,':'
- CALL32F SEL_CODE32,OUCH
- MOV EDX,DUMP_OFF
- MOV EAX,EDX
- CALL32F SEL_CODE32,HEXOUT4
- MOV ECX,DUMP_CNT
- DUMPLOOP:
- MOV AL,' '
- CALL32F SEL_CODE32,OUCH
- MOV EAX,ES:[EDX] ; get word
- CALL32F SEL_CODE32,HEXOUT4
- ADD EDX,4
- SUB ECX,4
- JA SHORT DUMPLOOP
- CALL32F SEL_CODE32,CRLF
- NOMEMDUMP:
-
-
- MOV AL,20H ; Send EOI signal
- IF ATCLASS
- OUT 0A0H,AL
- ENDIF
- OUT 20H,AL ; just in case hardware did it
- MOV AL,7FH ; return 7f to DOS
- BACK2DOS
-
- ; Here we check the GP fault
- ; if the mode isn't VM86 we do a debug dump
- ; Otherwise we try and emulate an instruction
- ; If the instruction isn't known, we do a debug dump
- INT13H:
- ADD ESP,4 ; balance stack (remove intno)
- TEST [ESP+12],20000H
- JZ SHORT SIM13A ; wasn't a vm86 interrupt!
- ADD ESP,4 ; remove error code
- PUSH EAX
- PUSH EBX
- PUSH DS
- PUSH EBP
- MOV EBP,ESP ; point to stack frame
- ADD EBP,10H
- MOV AX,SEL_DATA0
- MOV DS,AX
- MOV EBX,[EBP+4] ; get cs
- AND EBX,0FFFFH
- SHL EBX,4
- ADD EBX,[EBP] ; get eip
- XOR EAX,EAX ; al = OPCODE byte
- ; ah = # of bytes skipped over
- ; bit 31 of eax=1 if OPSIZ prefix
- ; encountered
- JMP SHORT INLOOP
-
- ; set sign bit of eax if OPSIZ
- FSET: OR EAX,80000000H
- INLOOP: MOV AL,[EBX]
- INC AH
- INC EBX
- CMP AL,66H ; opsize prefix
- JZ SHORT FSET
- ; scan for instructions
- CMP AL,9DH
- JZ SHORT DOPOPF
- CMP AL,9CH
- JZ SHORT DOPUSHF
- CMP AL,0FAH
- JZ NEAR PTR DOCLI
- CMP AL,0FBH
- JZ NEAR PTR DOSTI
- CMP AL,0CDH
- JZ NEAR PTR DOINTNN
- CMP AL,0CFH
- JZ NEAR PTR DOIRET
- CMP AL,0F0H
- JZ NEAR PTR DOLOCK
- ; Whoops! What the $#$%$#! is that?
- POP EBP
- POP DS
- POP EBX
- POP EAX
- SIM13:
- PUSH 0 ; simulate error
- SIM13A:
- PUSH 13 ; simulate errno
- JMP32S NOT13
-
- ;********************************************************************
- ; The following routines emulate VM86 instructions. Their conditions
- ; on entry are:
- ; eax[31]=1 iff opsiz preceeded instruction
- ; ah=count to adjust eip on stack
- ; al=instruction
- ; [EBX] next opcode byte
- ; ds: zerobase segment
-
-
- ; This routine emulates a popf
- DOPOPF:
- MOV BX, [EBP] ; fix IP
- ADD BL,AH
- ADC BH,0
- MOV [EBP],BX
- ; get ss*10H, add esp fetch top of stack
- MOVZX EBX,WORD PTR [EBP+10H]
- SHL EBX,4
- ADD EBX,[EBP+0CH]
- MOVZX EAX,WORD PTR [EBX]
- MOV EBX,[EBP+8] ; get his real flags
- AND BX,07000H ; only preserve NT,IOPL
- AND AX,08FFFH ; wipe NT,IOPL in new flags
- OR EAX,EBX
- MOV [EBP+8],EAX ; save his real flag image
- MOV EBX,2
- ADD [EBP+0CH],EBX
- MOV EBX,0FFFEFFFFH
- AND [EBP+8],EBX
- POP EBP
- POP DS
- POP EBX
- POP EAX
- IRETD
-
- ; Routine to emulate pushf
- DOPUSHF:
- MOV BX, [EBP] ; Fix ip
- ADD BL,AH
- ADC BH,0
- MOV [EBP],BX
- MOV EAX,[EBP+8] ; get his flags
- ; get ss, add esp and "push" flags
- MOVZX EBX,WORD PTR [EBP+10H]
- SHL EBX,4
- ADD EBX,[EBP+0CH]
- MOV [EBX-2],AX
- MOV EBX,2
- ; adjust stack
- SUB [EBP+0CH],EBX
- ; mask out flag bits
- MOV EBX,0FFFEFFFFH
- AND [EBP+8],EBX
- POP EBP
- POP DS
- POP EBX
- POP EAX
- IRETD
-
- ; Emulate CLI
- DOCLI:
- MOV BX, [EBP] ; Fix ip
- ADD BL,AH
- ADC BH,0
- MOV [EBP],BX
- MOV EAX,[EBP+8] ; get flags
- OR EAX,20000H ; set vm, clr RF & IOPL
- AND EAX,0FFFECDFFH
- MOV [EBP+8],EAX ; replace flags
- POP EBP
- POP DS
- POP EBX
- POP EAX
- IRETD
-
- ; Emulate STI
- DOSTI:
- MOV BX, [EBP] ; Fix ip
- ADD BL,AH
- ADC BH,0
- MOV [EBP],BX
- MOV EAX,[EBP+8] ; get flags
- OR EAX,20200H ; set vm, clr RF & IOPL
- AND EAX,0FFFECFFFH
- MOV [EBP+8],EAX ; replace flags
- POP EBP
- POP DS
- POP EBX
- POP EAX
- IRETD
-
-
- ; This routine emulates an INT nn instruction
- DOINTNN:
- PUSH EDX
- PUSH ECX
- ; get ss
- MOVZX EDX,WORD PTR [EBP+10H]
- SHL EDX,4
- ; add esp
- ADD EDX,[EBP+0CH]
- ; move flags, qsir address to vm86 stack & correct esp
- ; ... flags
- MOV CX, [EBP+08H]
- MOV [EDX-2],CX
- MOV WORD PTR [EDX-4],SEG QIRET
- MOV WORD PTR [EDX-6],OFFSET QIRET
- SUB DWORD PTR [EBP+0CH],6
- MOV CX, [EBP] ; ip
- INC AH ; adjust ip by # of bytes to skip
- ADD CL,AH
- ADC CH,0
- MOV [EBP],CX
- ; get tss alias (always directly above TSS in GDT)
- STR DX ; get our task #
- SUB DX,8 ; alias is one above
- MOV ES,DX
- MOV DX,SEL_DATA
- MOV DS,DX
- ASSUME DS:DAT32
- ; get pl0 esp from TSS & push to local stack
- MOV EDX,INTSP
- SUB EDX,4
- MOV INTSP,EDX
- MOV ECX,ES:[4] ; esp0
- MOV [EDX],ECX
- ; get int vector
- MOV DX,SEL_DATA0
- MOV DS,DX
- MOV ECX,ESP ; adjust stack for int 30H
- ADD ECX,60
- MOV ES:[4],ECX
- ; test for zero; if so called from int 30H
- OR AH,AH
- MOVZX EDX,AL
- JZ SHORT FROM30
- ; otherwise get int vector from CS:EIP stream
- MOVZX EDX,BYTE PTR [EBX]
- MOV ECX,ESP
- ADD ECX,24
- MOV ES:[4],ECX ; adjust stack for non-int 30H
- FROM30:
- ; interrupt vector*4 = VM86 interrupt vector address
- SHL EDX,2
- ; try to clean up mess on stack
- MOV AX,SEL_DATA
- MOV DS,AX
- MOV STO2,EDX
- POP ECX
- POP EDX
- XCHG STO2,EDX
- MOV STO1,ECX
- MOV STO3,EBP
- POP EBP
- XCHG STO3,EBP
- POP ECX
- MOV BX,SEL_DATA
- MOV DS,BX
- MOV STO4,ECX
- POP EBX
- POP EAX
- MOV CX,SEL_DATA0
- MOV DS,CX
- ; copy segment registers & esp for vm86 int
- PUSH [EBP+20H]
- PUSH [EBP+1CH]
- PUSH [EBP+18H]
- PUSH [EBP+14H]
- PUSH [EBP+10H]
- PUSH [EBP+0CH]
- MOV ECX,[EBP+08]
- ; push flags (with vm=1,iopl=0),cs, eip, rf=0
- OR ECX,20000H
- ; clear iopl, rf, tf, if and push flags
- AND ECX,0FFFECCFFH
- PUSH ECX
- ; read new cs/ip from 8086 idt
- ; ... push CS
- MOVZX ECX,WORD PTR [EDX+2]
- PUSH ECX
- ; ... push IP
- MOVZX ECX,WORD PTR [EDX]
- PUSH ECX
- MOV CX,SEL_DATA
- MOV DS,CX
- PUSH STO4
- MOV ECX,STO1
- MOV EDX,STO2
- MOV EBP,STO3
- POP DS
- IRETD ; go on to vm86 land
-
- ; Emulate IRET instruction
- DOIRET:
- ; vm86 stack
- MOVZX EAX,WORD PTR[EBP+10H]
- SHL EAX,4
- ADD EAX,[EBP+0CH]
- MOV EBX,[EAX] ; get cs:ip
- ; If top of stack=0:0 than a RETF or RETF 2 was detected
- OR EBX,EBX
- JZ SHORT FARRETINT
- PUSH ECX
- XOR ECX,ECX
- ; compare return address with QIRET
- MOV CX, SEG QIRET
- SHL ECX,16
- MOV CX,OFFSET QIRET
- CMP EBX,ECX
- POP ECX
- ; if equal than "normal" IRET
- JZ SHORT NORMIRET
-
- ; Not equal then that vm86 jerk is "faking" an IRET to pass control
- ; We must build a "fake" pl0 frame
- ; adjust sp
- ADD DWORD PTR [EBP+0CH],6
- ; get ip
- MOVZX EBX,WORD PTR [EAX]
- MOV [EBP],EBX
- ; get cs
- MOVZX EBX,WORD PTR [EAX+2]
- MOV [EBP+4],EBX
- ; get new flags
- MOVZX EBX,WORD PTR [EAX+4]
- OR EBX,20000H ; set vm, clr RF & IOPL
- AND EBX,0FFFECFFFH
- MOV [EBP+8],EBX
- POP EBP
- POP DS
- POP EBX
- POP EAX
- IRETD ; go on
-
- ; this means qiret caught a FAR RET instead of an IRET
- ; we must preserve our current flags!
- FARRETINT:
- MOV EAX,EBP
- POP EBP
- POP DS
- PUSH EBP
- PUSH EAX
- MOV BX,DS
- MOV AX,SEL_DATA
- MOV DS,AX
- MOV STO3,EBX
- POP EBP ; ISR's ebp
- MOV EAX,[EBP+0CH]
- ADD EAX,6 ; skip pushes from qiret
- MOV STO4,EAX
- ; get flags
- MOV EAX,[EBP+08H]
- MOV STO2,EAX
- JMP SHORT NIRET
-
- ; This handles the "normal" case
- NORMIRET:
- MOV BX,[EAX+4] ; get flags
- MOV EAX,EBP
- POP EBP
- POP DS
- PUSH EBP
- PUSH EAX
- MOV AX,BX
- MOV BX,DS
- PUSH SEL_DATA
- POP DS
- MOV STO2,EAX
- MOV STO3,EBX
- POP EBP ; ISR's ebp
- XOR EAX,EAX
- MOV STO4,EAX
- NIRET:
- PUSH ESI
- XOR ESI,ESI
- OR DWORD PTR [EBP+28H],0
- ; if CS=0 then int 30H asked for segment save
- JNZ SHORT V86IRET
- MOV EAX,[EBP+14H]
- MOV SAV_ES,EAX
- MOV EAX,[EBP+18H]
- MOV SAV_DS,EAX
- MOV EAX,[EBP+1CH]
- MOV SAV_FS,EAX
- MOV EAX,[EBP+20H]
- MOV SAV_GS,EAX
- MOV ESI,8
-
- V86IRET:
- MOV AX,ES
- MOV STO1,EAX
- POP EBP
- XCHG EBP,[ESP]
- ; get tss alias
- STR AX
- SUB AX,8
- MOV ES,AX
- ASSUME DS:DAT32
- MOV EAX,ES:[4] ; get our current stack begin
- ; see if we have to balance the VM86 stack
- TEST SS:[EAX+ESI+8],20000H
- JZ SHORT STKADJD
- MOV EBX,STO4
- OR EBX,EBX
- JZ SHORT ADJSTK
- ; balance vm86 stack
- MOV SS:[EAX+ESI+0CH], EBX
- JMP SHORT STKADJD
- ADJSTK: ADD DWORD PTR SS:[EAX+ESI+0CH],6
- STKADJD:
- ; get quasi flags
- MOV EBX,STO2
- ; get real flags
- PUSH SS:[EAX+ESI+8]
- ; preserve flags
- MOV DWORD PTR SS:[EAX+ESI+8],EBX
- LEAVEFLAGS:
- ; only let 8086 part of flags stay
- AND DWORD PTR SS:[EAX+ESI+08],01FFFH
- POP EBX ; load real flags into ebx
- ; save 386 portion of old flags (AND IP)
- AND EBX,0FFFFE200H
- OR SS:[EAX+ESI+8],EBX
- POP ESI
- XCHG EAX,[ESP]
- PUSH EAX ; stack = ebx, new sp
- MOV EBX,INTSP
- ; get prior pl0 esp from local stack
- MOV EAX,[EBX]
- ADD EBX,4
- MOV INTSP,EBX
- MOV ES:[4],EAX ; restore to TSS
- ; restore registers
- POP EBX
- MOV ES,WORD PTR STO1
- MOV DS,WORD PTR STO3
- POP EAX ; restore "real" eax
- XCHG EAX,[ESP]
- POP ESP ; set up new top stack
- XCHG EAX,[ESP+4]
- OR EAX,EAX ; test cs
- XCHG EAX,[ESP+4]
- JNZ SHORT GOIRET
- ADD ESP,8 ; skip fake CS/IP from INT 30H
- GOIRET:
- ; reset resume flag
- AND DWORD PTR [ESP+8],0FFFECFFFH
- IRETD
-
-
-
-
- ; Emulate lock prefix
- DOLOCK:
-
- POP EBP
- POP DS
- POP EBX
- POP EAX
- PUSH 0FFFFH
- PUSH 13 ; simulate errno
- JMP32S NOT13
-
-
- ; This is the interface routine to allow a protected mode
- ; program call VM86 interrupts.
- ; Call with es:ebx pointing to a parameter block
- ; +00 flag - if 1 then resave ES, DS, FS & GS
- ; into parameter block after call
- ; +04 int number (0-255) (required)
- ; +08 eflags
- ; +12 vm86 esp (required)
- ; +16 vm86 ss (required)
- ; +20 vm86 es
- ; +24 vm86 ds
- ; +28 vm86 fs
- ; +32 vm86 gs
- ; +36 vm86 ebp ( to replace that used in call )
- ; +40 vm86 ebx ( to replace that used in call )
- ;
- ; all other registers will be passed to vm86 routine
- ;
- ; This routine depends on the dointnn routine
-
- INT30H:
- ADD ESP,4 ; remove intno
- CMP BYTE PTR ES:[EBX],0
- JZ SHORT NOSEGSAV
- ; dummy CS/IP to signal IRET to save segments
- PUSH 0
- PUSH 0
- NOSEGSAV:
- PUSH ES:[EBX+32] ; stack up registers
- PUSH ES:[EBX+28]
- PUSH ES:[EBX+24]
- PUSH ES:[EBX+20]
- PUSH ES:[EBX+16]
- PUSH ES:[EBX+12]
- ; force VM86=1 in EFLAGS
- XCHG EAX,ES:[EBX+8]
- OR EAX,20000H
- AND EAX,0FFFECFFFH
- PUSH EAX
- XCHG EAX,ES:[EBX+8]
- PUSH 0 ; don't care cs
- PUSH 0 ; don't care eip
- MOV EBP,ESP
- PUSH EAX
- PUSH ES:[EBX+40] ; vm86 ebx
- PUSH DS
- PUSH ES:[EBX+36] ; vm86 ebp
- MOV AX,SEL_DATA0
- MOV DS,AX
- ; get user's intno
- MOV AL,ES:[EBX+4]
- ; set flag to dointnn not to check cs:ip for int #
- MOV AH,0FFH
- ; go ahead.... make my interrupt
- JMP32S DOINTNN
-
- ; handle hardware int!
- ; This routine uses INT 30 to handle HW interrupts
- ; If interrupted in protected mode, a special stack
- ; is used. If in VM86 mode, the current VM86 stack is used
- HWINT:
- XCHG EAX,[ESP] ; swap eax & int #
- PUSH DS
- PUSH ES
- PUSH EBX
- MOV BX,SEL_DATA
- MOV DS,BX
- MOV ES,BX
- CMP EAX,28H
- JB SHORT IRQ07
- ADD EAX,48H ; vector IRQ8-F to INT 70-77
- JMP SHORT IRQSET
- IRQ07:
- SUB EAX,24 ; vector IRQ0-7 to INT 8-0F
- IRQSET:
- ; set up special interrupt frame
- MOV HINTFRAME.VMINT,EAX
- MOV HINTFRAME.VMEBP,EBP
- POP EBX
- MOV HINTFRAME.VMEBX,EBX
- PUSH EBX
- MOV EAX,020000H ; model flags
- MOV HINTFRAME.VMFLAGS,EAX
- MOV EAX,OFFSET SSINT1
- MOV HINTFRAME.VMESP,EAX
- MOV AX,SEG SSINT1
- MOV HINTFRAME.VMSS,EAX
- MOV EAX,[ESP+24] ; get flags
- TEST EAX,20000H ; check vm
- JZ SHORT NOTVMHW
- MOV EAX,[ESP+28] ; get vm86's esp
- MOV HINTFRAME.VMESP,EAX
- MOV EAX,[ESP+32]
- MOV HINTFRAME.VMSS,EAX
- NOTVMHW:
- MOV EBX,OFFSET HINTFRAME
- PUSH FS
- PUSH GS
- INT 30H ; Do interrupt
- POP GS
- POP FS
- POP EBX
- POP ES
- POP DS
- POP EAX
- IRETD
-
- ISREND EQU $
- ISR ENDS
-
-
-
-
- [LISTING SIX]
-
- ;********************************************************************
- ;* *
- ;* PROT - A 386 protected mode DOS extender *
- ;* Copyright (C) 1989, by Al Williams *
- ;* All rights reserved. *
- ;* *
- ;* Permission is granted for non-commercial use of this software *
- ;* subject to certain conditions (see PROT.ASM). *
- ;* *
- ;* This file is: TSS.INC, the Task State Segment definitions. *
- ;* *
- ;********************************************************************
- ; define TSS structure
- ; for more details refer to the Intel documentation
- ; remember, the defined values are only defaults, and
- ; can be changed when a value is defined
- TSSBLK STRUC
- BLINK DD 0
- ESPP0 DD OFFSET SSEG321
- SSP0 DD SEL_STACK
- ESPP1 DD 0
- SSP1 DD SEL_STACK
- ESPP2 DD 0
- SSP2 DD SEL_STACK
- CR31 DD 0
- EIP1 DD OFFSET USER
- EF1 DD 200H
- EAX1 DD 0
- ECX1 DD 0
- EDX1 DD 0
- EBX1 DD 0
- ESP1 DD OFFSET SSEG321
- EBP1 DD 0
- ESI1 DD 0
- EDI1 DD 0
- ES1 DD SEL_DATA
- CS1 DD SEL_UCODE
- SS1 DD SEL_STACK
- DS1 DD SEL_UDATA
- FS1 DD SEL_DATA0
- GS1 DD SEL_VIDEO
- LDT1 DD 0
- DW 0
- IOT DW $+2-OFFSET BLINK
- IOP DB 8192 DUP (0)
- DB 0FFH
- TSSBLK ENDS
-
-
- TSSSEG SEGMENT PARA PUBLIC 'DATA32' USE16
- ORG 0
- ; Dummy TSS that stores the original machine state
- TSS0BEG TSSBLK <>
- TSS0END EQU $
-
- ; TSS to run the USER task
- TSS1BEG TSSBLK <>
- TSS1END EQU $
-
- TSSSEG ENDS
-
-
- [LISTING SEVEN]
-
- ;********************************************************************
- ;* *
- ;* PROT - A 386 protected mode DOS extender *
- ;* Copyright (C) 1989, by Al Williams *
- ;* All rights reserved. *
- ;* *
- ;* Permission is granted for non-commercial use of this software *
- ;* subject to certain conditions (see PROT.ASM). *
- ;* *
- ;* This file is: CODE16.INC, the 16 bit DOS entry/exit code. *
- ;* *
- ;********************************************************************
-
- CSEG SEGMENT PARA PUBLIC 'CODE16' USE16
- ASSUME CS:CSEG, DS:CSEG
- BEG16 EQU $
- IDTSAV DF 0 ; space to save old real mode IDT
- XZRO DF 0 ; Zero constant for inhibiting IDT
-
- TEMP EQU THIS FWORD ; Space to load GDT
- TLIM DW (GDTEND-GDT)-1
- TEMD DD 0
-
- ; area to save stack pointer
- SOFFSAV DW 0
- SSEGSAV DW 0
-
- ; old keyboard interrupt vector -- we have to catch reboot requests
- KEYCHAIN EQU THIS DWORD
- KEYOFF DW ?
- KEYSEG DW ?
-
- INTM DB 0 ; interrupt mask - pic 1
- IF ATCLASS
- INTMAT DB 0 ; interrupt mask - pic 2 (AT ONLY)
- ENDIF
-
- ;psp
- PSP DW 0
-
- ; error messages
- NOT386M DB 'Error: this program requires an 80386 or 80486'
- DB ' processor.',13,10,'$'
- VM86M DB 'Error: this program will not execute '
- DB 'in VM86 mode.'
- DB 13,10,'$'
-
- ; 16 bit ss/sp for return to real mode
- LOAD16 DD OFFSET SSEG1-1
- DW SEL_RDATA
-
- ;****** Begin program
- ENTRY LABEL FAR
- START PROC NEAR
- PUSH CS ; set up DS segment, save PSP
- POP DS
- MOV AX,ES
- MOV PSP,AX ; save PSP
- MOV BX,DAT32
- MOV ES,BX
- MOV ES:_PSP,AX
- ; check to see if we are running on a 386/486
- XOR AX,AX
- PUSH AX
- POPF
- PUSHF
- POP AX
- AND AX,0F000H
- CMP AX,0F000H
- JNZ SHORT NOT86
- NOT386:
- MOV DX, OFFSET NOT386M
- NOT386EXIT:
- MOV AH,9
- INT 21H
- MOV AX,4C80H
- INT 21H ; exit
- NOT86:
- MOV AX,0F000H
- PUSH AX
- POPF
- PUSHF
- POP AX
- AND AX,0F000H
- JZ SHORT NOT386
- ; If we got here we are on an 80386.
- ; Check PM flag
- SMSW AX
- AND AX,1 ; are we in protected mode?
- MOV DX,OFFSET VM86M
- JNZ SHORT NOT386EXIT
- ; OK.. we are clear to proceed
-
- ; Set up new ^C, keyboard and Critical error handlers
- MOV AX,3509H
- INT 21H
- MOV AX,ES
- MOV KEYSEG,AX
- MOV KEYOFF,BX
- MOV AX,2509H
- MOV DX,OFFSET REBOOT
- INT 21H
- MOV AX,2523H
- MOV DX,OFFSET CTRLC
- INT 21H
- MOV AX,2524H
- MOV DX,OFFSET CRITERR
- INT 21H
- ; * Create segments
- PUSH GDTSEG
- POP ES
- MOV EDX, OFFSET GDT
- MOV EBX,CS
- SHL EBX,4 ; calc segment base address
- MOV ECX,0FFFFH ; 64 K limit (don't change)
- MOV AH,ER_CODE ; read/exec code seg
- XOR AL,AL ; size
- PUSH GDTSEG
- POP ES
- MOV EDX, OFFSET GDT
- MOV SI,SEL_CODE16
- CALL MAKE_DESC ; make code seg (16 bit/real)
- MOV ECX,0FFFFFH
- XOR EBX,EBX
- MOV SI,SEL_DATA0
- XOR ECX,ECX
- DEC ECX ; ecx=ffffffff
- MOV AL,1
- MOV AH,RW_DATA
- CALL MAKE_DESC ; make data ( 4G @ zero base )
- XOR EAX,EAX
- INT 12H
- MOVZX ECX,AX
- SHL ECX,10
- LOADFREE BX ; get free memory segment
- SUB ECX,EBX
- DEC ECX
- MOV SI,SEL_FREE
- MOV AL,1
- MOV AH,RW_DATA
- CALL MAKE_DESC
- XOR EAX,EAX
- MOV AH,88H ; get top of extended memory
- INT 15H
- SHL EAX,10 ; * 1024
- OR EAX,EAX ; any extended present?
- MOV ECX,EAX
- JNZ SHORT EXTPRES
- MOV ECX,1
- EXTPRES:
- DEC ECX
- MOV EBX,100000H
- MOV SI,SEL_EXT ; 0 limit segment if no ext.
- MOV AL,1
- MOV AH,RW_DATA
- CALL MAKE_DESC
- XOR EBX,EBX
- MOV BX,SEG SEG32ENT
- SHL EBX,4
- MOV ECX,(SEG32END-SEG32BEG)-1
- MOV AH,ER_CODE
- MOV AL,1
- MOV SI,SEL_CODE32
- CALL MAKE_DESC ; 32 bit code segment
- XOR EBX,EBX
- MOV BX,USERCODE
- SHL EBX,4
- MOV ECX,(USERCODEEND-USERCODEBEG)-1
- MOV AH,ER_CODE
- MOV AL,1
- MOV SI,SEL_UCODE
- CALL MAKE_DESC
- XOR EBX,EBX
- MOV BX,USERDATA
- SHL EBX,4
- MOV ECX,(USERDATAEND-USERDATABEG)-1
- MOV AH,RW_DATA
- MOV AL,1
- MOV SI,SEL_UDATA
- CALL MAKE_DESC
- XOR EBX,EBX
- MOV BX,SS32
- SHL EBX,4 ; always para align stacks!
- MOV ECX,(SSEG321-SSEG32)-1
- MOV AH,RW_DATA ; stack seg is data type
- MOV AL,1
- MOV SI,SEL_STACK
- CALL MAKE_DESC
- XOR EBX,EBX
- MOV BX, SSEG
- SHL EBX,4
- MOV ECX,0FFFFH ; real mode limit (don't change)
- XOR AL,AL
- MOV AH,RW_DATA
- MOV SI,SEL_RDATA
- CALL MAKE_DESC ; 16 bit data for return to r/m
- XOR EBX,EBX
- MOV BX,SEG GDT
- SHL EBX,4
- ADD EBX,OFFSET GDT
- MOV ECX,(GDTEND-GDT)-1
- MOV AL,1
- MOV AH,RW_DATA
- MOV SI,SEL_GDT
- CALL MAKE_DESC
- MOV AX,500H ; set video to page 0
- INT 10H
- MOV AH,0FH
- INT 10H ; get mode
- MOV EBX,0B0000H ; monochrome
- CMP AL,7 ; check for mono
- JZ SHORT VIDEOCONT
- MOV EBX,0B8000H
- VIDEOCONT:
- MOV ECX,3999 ; limit for text page
- MOV AL,1
- MOV AH,RW_DATA
- MOV SI,SEL_VIDEO
- CALL MAKE_DESC ; make video segment
- XOR EBX,EBX
- MOV BX,DAT32
- SHL EBX,4
- MOV ECX,(DAT32END-DAT32BEG)-1
- MOV AH,RW_DATA
- MOV AL,1
- MOV SI,SEL_DATA
- CALL MAKE_DESC
- XOR EBX,EBX
- MOV BX,IDTABLE
- SHL EBX,4
- MOV ECX,(IDTEND-IDTBEG)-1
- MOV AH,RW_DATA
- MOV AL,1
- MOV SI,SEL_IDT
- CALL MAKE_DESC
- XOR EBX,EBX
- MOV BX,ISR
- SHL EBX,4
- MOV ECX,(ISREND-ISRBEG)-1
- MOV AH,ER_CODE
- MOV AL,1
- MOV SI,SEL_ICODE
- CALL MAKE_DESC
- XOR EBX,EBX
- MOV BX,TSSSEG
- SHL EBX,4
- ; compute TSS length
- MOV ECX,(TSS0END-TSS0BEG)-1
- MOV AH,RW_DATA
- MOV AL,1
- MOV SI,SEL_TSS0
- CALL MAKE_DESC
- MOV AH,TSS_DESC
- MOV SI,TSS0
- CALL MAKE_DESC
- ADD EBX,OFFSET TSS1BEG
- MOV SI,TSS1
- ; compute TSS length
- MOV ECX,(TSS1END-TSS1BEG)-1
- CALL MAKE_DESC
- MOV SI,SEL_TSS1
- MOV AH,RW_DATA
- CALL MAKE_DESC
- MOVZX EBX,PSP
- SHL EBX,4
- MOV ECX,255
- MOV AH,RW_DATA
- MOV AL,1
- MOV SI,SEL_PSP
- CALL MAKE_DESC
- PUSH ES
- MOV AX,PSP
- MOV ES,AX
- XOR EBX,EBX
- MOV BX,ES:[2CH]
- MOV AX,BX
- SHL EBX,4
- DEC AX
- MOV ES,AX
- XOR ECX,ECX
- MOV CX,ES:[3]
- SHL ECX,4
- DEC ECX ; get limit
- MOV SI,SEL_ENV
- POP ES
- MOV AL,1
- MOV AH,RW_DATA
- CALL MAKE_DESC ; make envrioment segment
-
- ; turn on A20
- MOV AL,1
- CALL SETA20
- CLI ; no interrupts until prot mode
- MOV SSEGSAV,SS
- ; save sp for triumphant return to r/m
- MOV SOFFSAV,SP
- SIDT IDTSAV
- LIDT XZRO ; save and load IDT
- XOR EBX,EBX
- MOV BX,SEG GDT
- SHL EBX,4
- ADD EBX,OFFSET GDT
- MOV TEMD,EBX
- LGDT TEMP ; set up GDT
- MOV EAX,CR0
- OR EAX,1 ; switch to prot mode!
- MOV CR0,EAX
- ; jump to load CS and flush prefetch
- JMPABS SEL_CODE16,PROT1
-
- PROT1:
- OPSIZ
- JMPABS32 SEL_CODE32,SEG32ENT
-
-
-
- ; Jump here to return to real mode DOS.
- ; If desired AL can be set to a DOS exit code
- BACK16 LABEL FAR
- MOV BL,AL ; save exit code
- CLI
- XOR EAX,EAX
- MOV DR7,EAX ; turn off debug (just in case)
- ; restore stack
- LSS ESP,FWORD PTR CS:LOAD16
- MOV AX,SEL_RDATA
- MOV DS,AX
- MOV ES,AX
- MOV FS,AX
- MOV GS,AX
- MOV EAX,CR0
- ; return to real mode
- AND EAX,07FFFFFF2H
- MOV CR0,EAX
- ; jump to load CS and clear prefetch
- JMPABS CSEG,NEXTREAL
- NEXTREAL LABEL FAR
- MOV AX,CS
- MOV DS,AX
- LIDT IDTSAV ; restore old IDT 0(3ff)
- IN AL,21H
- MOV INTM,AL
- ; reprogram PIC's
- IF ATCLASS
- IN AL,0A1H
- MOV INTMAT,AL
- MOV AL,11H
- OUT 0A0H,AL
- OUT 20H,AL
- IDELAY
- MOV AL,70H
- OUT 0A1H,AL
- MOV AL,8
- OUT 21H,AL
- IDELAY
- MOV AL,2
- OUT 0A1H,AL
- MOV AL,4
- OUT 21H,AL
- IDELAY
- MOV AL,1
- OUT 0A1H,AL
- OUT 21H,AL
- IDELAY
- MOV AL,INTMAT
- OUT 0A1H,AL
- MOV AL,INTM
- OUT 21H,AL
-
- ELSE
-
- MOV AL,13H
- OUT 20H,AL
- MOV AL,8
- OUT 21H,AL
- INC AL
- OUT 21H,AL
- MOV AL,INTM
- OUT 21H,AL
- ENDIF
- ; clean up to go back to DOS
- LSS SP,DWORD PTR SOFFSAV
- STI ; resume interupt handling
- ; turn a20 back off
- XOR AL,AL
- CALL SETA20
- ; restore keyboard interrupt
- MOV DX,KEYOFF
- MOV AX,KEYSEG
- PUSH DS
- MOV DS,AX
- MOV AX,2509H
- INT 21H
- POP DS
- MOV AH,4CH ; blow this joint!
- MOV AL,BL ; get return code
- INT 21H ; return to the planet of MSDOS
- START ENDP
-
-
- ; Routine to control A20 line
- ; AL=1 to turn A20 on (enable)
- ; AL=0 to turn A20 off (disable)
- ; returns ZF=1 if error; AX destroyed
- IF ATCLASS
- SETA20 PROC NEAR
- PUSH CX
- MOV AH,0BCH ; A20 On
- OR AL,AL
- JNZ A20WAIT1
- MOV AH,0B4H ; A20 Off
- A20WAIT1:
- CALL KEYWAIT
- JZ A20ERR
- MOV AL,AH
- OUT 64H,AL
- CALL KEYWAIT
- JZ A20ERR
- MOV AL,AH
- OUT 64H,AL
- CALL KEYWAIT
- A20ERR: POP CX
- RET
- SETA20 ENDP
-
- ; Wait for keyboard controller ready. Returns ZF=1 if timeout
- ; destroys CX and AL
- KEYWAIT PROC NEAR
- XOR CX,CX ; maximum time out
- KWAITLP:
- DEC CX
- JZ KEYEXIT
- IN AL,64H
- AND AL,2
- JNZ KWAITLP
- KEYEXIT: OR CX,CX
- RET
- KEYWAIT ENDP
-
- ELSE
- ; INBOARD PC Code for A20
- SETA20 PROC NEAR
- OR AL,AL
- MOV AL,0DFH
- JNZ A20SET
- MOV AL,0DDH
- A20SET: OUT 60H,AL
- OR AL,AL ; make sure ZF is set for
- RET ; compatibilty with AT routines
- SETA20 ENDP
- ENDIF
-
-
- ; This routine makes a descriptor
- ; ebx=base
- ; ecx=limit in bytes
- ; es:edx=GDT address
- ; al= size (0=16bit 1=32bit)
- ; ah=AR byte
- ; SI=descriptor (TI & DPL not important!)
- ; Auto sets and calculates G and limit
- MAKE_DESC PROC NEAR
- PUSHAD
- MOVZX ESI,SI
- SHR SI,3 ; adjust to slot #
- SHL AL,6 ; shift size to right bit position
- CMP ECX,0FFFFFH ; see if you need to set G bit
- JBE OKLIMR
- SHR ECX,12 ; div by 4096
- OR AL,80H ; set G bit
- OKLIMR: MOV ES:[EDX+ESI*8],CX
- SHR ECX,16
- OR CL,AL
- MOV ES:[EDX+ESI*8+6],CL
- MOV ES:[EDX+ESI*8+2],BX
- SHR EBX,16
- MOV ES:[EDX+ESI*8+4],BL
- MOV ES:[EDX+ESI*8+5],AH
- MOV ES:[EDX+ESI*8+7],BH
- POPAD
- RET
- MAKE_DESC ENDP
-
- ; This is the routine that disables ^C interrupts
- ; You could place your own code here if desired
- ; NOTE: THIS IS VM86 CODE!
- CTRLC PROC FAR
- PUSH DS
- PUSH AX
- MOV AX,DAT32
- MOV DS,AX
- ASSUME DS:DAT32
- MOV AL,1
- MOV BREAKKEY,AL ; set flag
- POP AX
- POP DS
- IRET
- CTRLC ENDP
-
- ; Reboot handler (VM86 code)
- REBOOT PROC FAR
- STI
- PUSH AX
- IN AL,60H
- CMP AL,53H ; delete key?
- JNZ SHORT NOREBOOT
- XOR AX,AX
- PUSH DS
- MOV DS,AX
- MOV AL,DS:[417H] ; get shift status
- POP DS
- TEST AL,8 ; check for cntl/alt
- JZ SHORT NOREBOOT
- TEST AL,4
- JZ SHORT NOREBOOT
- ; If detected a ^ALT-DEL then eat it and return
- IN AL,61H
- MOV AH,AL
- OR AL,80H
- OUT 61H,AL
- MOV AL,AH
- OUT 61H,AL
- MOV AL,20H
- OUT 20H,AL
- POP AX
- IRET
- ; not a ^ALT-DEL, resume normal keyboard handler
- NOREBOOT: POP AX
- JMP CS:[KEYCHAIN]
- REBOOT ENDP
-
- ; Critical error handler (always fail/ignore)
- CRITERR PROC FAR
- PUSH DS
- PUSH DAT32
- POP DS
- ASSUME DS:DAT32
- MOV CRITAX,AX
- MOV AL,1
- MOV CRITICAL,AL
- MOV CRITDI,DI
- MOV CRITBP,BP
- MOV CRITSI,SI
- IF DOS LT 3
- XOR AL,AL
- ELSE
- MOV AL,3
- ENDIF
- POP DS
- IRET
- CRITERR ENDP
-
- LAST16 EQU $
- CSEG ENDS
-
-
- [Example 1: A simple PROT program.]
-
-
- File: USER.INC
- ; SET UP EMPTY DATA SEGMENT
- NODATA
-
- ; SET UP CODE SEGMENT - PROGRAM RETURNS TO DOS
- PROT_CODE
- USER PROC NEAR
- BACK2DOS
- USER ENDP
- PROT_CODE_END
-
-
-
- [Examplσ 2║ DO╙ anΣ PRO╘ codσ fragment≤ t∩ prin⌠ ß messagσ using
- DOS service 9.]
-
-
- Real Mode Program
- REALPGM PROC
- MOV AX,SEG STACKAREA
- MOV SS,AX
- MOV SP,OFFSET STACKAREA ; SET UP STACK
- MOV AX,SEG DATSEG
- MOV DS,AX ; SET UP DATA SEGMENT
- MOV DX,OFFSET MESSAGE ; LOAD POINTER TO MESSAGE
- MOV AH,9
- INT 21H ; PRINT MESSAGE
- MOV AH,4CH
- INT 21H ; RETURN TO DOS
- REALPGM ENDP
-
-
-
- PROT Equivalent
- USER PROC
- PROT_STARTUP ; SET UP STACK/DS
- MOV AX,21H
- MOV PINTFRAME.VMINT,EAX
- MOV EDX,OFFSET MESSAGE ; LOAD POINTER TO MESSAGE
- MOV AH,9
- MOV EBX,OFFSET PINTFRAME
- VM86CALL ; PRINT MESSAGE
- BACK2DOS ; RETURN TO DOS
- USER ENDP
-
-
-
-
- [Example │║ Maintaining the caller's flags on áthe ástack áwhen ì
- returning in protected mode.]
-
- MOV EAX,25H
- MOV PINTFRAME.VMINT,EAX
- PUSHF ; (Or PUSHFD)
- VM86CALL ; Call INT 25 or 26
- .
- .
-
-
-
- [Example 4: 32-bit offset generation problems]
-
- .386P
- SEGMENT EXAMPLE PARA 'CODE32' USE32
- .
- BACKWARD:
- .
- .
- .
- CMP EBX,EAX
- JA FORWARD ; This jump is OK
- JB BACKWARD ; This jump is improperly assembled
- .
- .
- .
- FORWARD:
-
-
- [Example 5: QISR code for the VM86 mode segment
- qisr segment para 'CODE16' use16]
-
- assume cs:qisr
- qiret:
- push 0
- push 0
- push 0
- iret
- qisr ends
-
-
- [Figure 1: Parameter block for call86 routine.]
-
- Address Member name
-
- BLOCK+0 |------------------------------------| VMSEGFLAG
- | Segment register flag (see text) |
- BLOCK+4 |------------------------------------| VMINT
- | Interrupt number |
- BLOCK+8 |------------------------------------| VMFLAGS
- | EFLAGS |
- BLOCK+12 |------------------------------------| VMESP
- | ESP |
- BLOCK+16 |------------------------------------| VMSS
- | SS |
- BLOCK+20 |------------------------------------| VMES
- | ES |
- BLOCK+24 |------------------------------------| VMDS
- | DS |
- BLOCK+28 |------------------------------------| VMFS
- | FS |
- BLOCK+32 |------------------------------------| VMGS
- | GS |
- BLOCK+36 |------------------------------------| VMEBP
- | EBP |
- BLOCK+40 |------------------------------------| VMEBX
- | EBX |
- |------------------------------------|
-
-
-
- [Figure 2: Batch file used to compile a PROT program]
-
- echo off
- if X%1==X goto :errexit
- if NOT X%2==X goto :errexit
- masm /DPROGRAM=%1 PROT.ASM,%1.OBJ,%1.LST;
- if ERRORLEVEL 1 goto :exit
- link %1;
- goto :exit
- :errexit
- echo PMASM - An MASM driver for the PROT 386 DOS Extender
- echo usage: PMASM progname
- echo Assembles the file progname.pm into progname.exe
- echo The PROT system is copyright (C), 1989 by Al Williams.
- echo Please see the file "PROT.ASM" for more details.
- :exit
-
-
-
- [Figure 3: PROT interrupt/breakpoint display]
-
- ES=0040 DS=0090 FS=0010 GS=0038
- EDI=00000000 ESI=00000000 EBP=00000000 ESP=00000FF0 EBX=00000000
- EDX=00000000 ECX=00000000 EAX=00000000 INT=03 TR=0070
- Stack Dump:
- 0000002B 00000088 00000202
-
-
-
-
- [Figure 4: Problem cases associated with software interrupts]
-
-
- Case 1: Normal INT/IRET
- ...
-
- INT 10H ; perform interrupt
- ...
- ISR: ; Interrupt 10H service routine
- ...
- IRET
-
-
- Case 2: INT/RETF 2
- ...
-
- INT 10H ; perform interrupt
- ...
- ISR: ; Interrupt 10H service routine
- ...
-
- RETF 2
-
-
- Case 3: INT/RETF (only used by INT 25H and 26H)
- ...
-
- INT 10H ; perform interrupt
- ...
- ISR: ; Interrupt 10H service routine
- ...
-
- RETF
-
-
- Case 4: PUSHF/FAR CALL
- ...
-
- PUSHF ; simulate interrupt
- CALL FAR ISR
- ...
-
- ISR: ;Interrupt 10H service routine
- ...
-
- IRET
-
-
- Case 5: PUSHF/PUSH ADDRESS/IRET
-
- ...
-
- PUSHF ; Jump to address TARGET
- PUSH SEG TARGET
- PUSH OFFSET TARGET
- IRET
- ...
- TARGET: ; Destination of IRET
- ...
-
- -or-
-
- PUSHF ; Simulate interrupt
- PUSH SEG RETAD
- PUSH OFFSET RETAD
- JMP FAR ISR
- RETAD:
- ...
-
- ISR: ; Interrupt routine
- ...
-
- IRET
-
-